package ru.lukyanets.transmission.client.ui.controller;

import com.google.common.eventbus.EventBus;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.util.Duration;
import ru.lukyanets.transmission.client.connection.ConnectionState;
import ru.lukyanets.transmission.client.connection.TransmissionService;
import ru.lukyanets.transmission.client.ui.control.ProgressBarTextTableCell;
import ru.lukyanets.transmission.client.ui.events.ChangeSelectedTorrentEvent;
import ru.lukyanets.transmission.client.util.LocalizationUtil;
import ru.lukyanets.transmission.protocol.client.exception.TransmissionException;
import ru.lukyanets.transmission.protocol.model.torrent.Torrent;
import ru.lukyanets.transmission.protocol.model.torrent.TrackerStat;

import com.google.inject.Inject;
import java.net.URL;
import java.util.Objects;
import java.util.ResourceBundle;

import static ru.lukyanets.transmission.client.util.UnitsUtil.getSizeText;
import static ru.lukyanets.transmission.client.util.UnitsUtil.getSpeedText;

/**
 * @author Sergey Lukyanets
 */
public class TorrentsTableController implements Initializable {
    @Inject
    private TransmissionService transmissionService;

    @SuppressWarnings("UnstableApiUsage")
    @Inject
    private EventBus eventBus;

    @FXML
    private TableView<Torrent> torrentsTable;
    @FXML
    private TableColumn<Torrent, String> sizeColumn;
    @FXML
    private TableColumn<Torrent, Double> completenessColumn;
    @FXML
    private TableColumn<Torrent, String> statusColumn;
    @FXML
    private TableColumn<Torrent, String> seedsColumn;
    @FXML
    private TableColumn<Torrent, String> peersColumn;
    @FXML
    private TableColumn<Torrent, String> downloadColumn;
    @FXML
    private TableColumn<Torrent, String> uploadColumn;
    @FXML
    private TableColumn<Torrent, String> priorityColumn;

    private Timeline timeline;
    private ObservableList<Torrent> torrents = FXCollections.observableArrayList();
    private ObjectProperty<Torrent> selected = new SimpleObjectProperty<>();

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        torrentsTable.setItems(torrents);
        torrentsTable.setPlaceholder(new Label());
        sizeColumn.setCellValueFactory(cell -> new SimpleStringProperty(getSizeText(cell.getValue().getTotalSize())));
        completenessColumn.setCellValueFactory(cell -> new SimpleObjectProperty<>(cell.getValue().getPercentDone()));
        completenessColumn.setCellFactory(cell -> new ProgressBarTextTableCell<>());
        statusColumn.setCellValueFactory(cell -> new SimpleStringProperty(LocalizationUtil.getString("torrent.status." + cell.getValue().getStatus().name())));
        seedsColumn.setCellValueFactory(cell -> new SimpleStringProperty(getSeedsText(cell.getValue())));
        peersColumn.setCellValueFactory(cell -> new SimpleStringProperty(getPeersText(cell.getValue())));
        downloadColumn.setCellValueFactory(cell -> new SimpleStringProperty(getSpeedText(cell.getValue().getRateDownload())));
        uploadColumn.setCellValueFactory(cell -> new SimpleStringProperty(getSpeedText(cell.getValue().getRateUpload())));
        priorityColumn.setCellValueFactory(cell -> new SimpleStringProperty(LocalizationUtil.getString("torrent.priority." + cell.getValue().getBandwidthPriority().name())));

        timeline = new Timeline(new KeyFrame(Duration.seconds(4), (e) -> refreshTable()));
        timeline.setCycleCount(Timeline.INDEFINITE);

        transmissionService.addListener((o, oldV, newState) -> Platform.runLater(() -> {
            if (ConnectionState.CONNECTED.equals(newState)) {
                refreshTable();
                timeline.play();
            } else {
                timeline.stop();
                clearTable();
            }
        }));

        torrentsTable.getSelectionModel().selectedItemProperty().addListener((o, oldV, newV) -> {
            if(newV != null && !Objects.equals(selected.get(), newV)) {
                selected.setValue(newV);
            }
        });

        selected.addListener((o, oldV, newV) -> {
            if (!Objects.equals(oldV, newV)) {
                eventBus.post(new ChangeSelectedTorrentEvent(oldV, newV));
            }
        });
    }

    private void refreshTable() {
        try {
            torrents.clear();
            torrents.addAll(transmissionService.getTorrents());
            if (torrents.contains(selected.get())) {
                torrentsTable.getSelectionModel().select(selected.get());
            } else {
                torrentsTable.getSelectionModel().select(null);
                selected.setValue(null);
            }
        } catch (TransmissionException ignore) {
        }
    }

    private void clearTable() {
        torrents.clear();
        selected.setValue(null);
    }

    @SuppressWarnings("Duplicates")
    private String getPeersText(Torrent torrent) {
        int leechers = torrent.getTrackerStats().stream().map(TrackerStat::getLeecherCount).findFirst().orElse(-1);
        String text = String.valueOf(torrent.getPeersGettingFromUs());
        if (leechers != -1) {
            text += "/" + leechers;
        }
        return text;
    }

    @SuppressWarnings("Duplicates")
    private String getSeedsText(Torrent torrent) {
        int seeders = torrent.getTrackerStats().stream().map(TrackerStat::getSeederCount).findFirst().orElse(-1);
        String text = String.valueOf(torrent.getPeersSendingToUs());
        if (seeders != -1) {
            text += "/" + seeders;
        }
        return text;
    }
}
